/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frreport_mng.c                                     */
/*  NOTE      = 上报数据模块                                       */
/*  DATE      = 2014/11/20                                         */
/*******************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <sys/prctl.h>

#include "frcomm.h"
#include "frlog.h"
#include "frmem.h"
#include "frxml_parse.h"

#include "module.h"
#include "frame.h"
#include "frame_params.h"
#include "frreport_mng.h"
#include "frct_comm.h"
#include "frlua_hook.h"
#include "frstat_conn.h"
#include "frlua_config.h"

#define APM_REPORT_INFO_NEW    "report_info_new"

typedef int (*FP_CALC_REPORT_NEW)(Report_module *);

extern void *g_p_memstat;

FR_STATIC Report_module g_st_report_module = {NULL, NULL, NULL, NULL, NULL};
FR_STATIC void  *g_p_so_handle=NULL;

/*******************************************************************************
*  FUNC     :  初始化report管理模块
*  ARGS     :
*  RTN      :  成功:FR_SUCC; 失败:FR_FAIL
*  NOTE     :
*******************************************************************************/
int  frreport_mng_init(void)
{
    int i_ret_fun;
    char *p_so_name=NULL, *p_so_params=NULL;
    FP_CALC_REPORT_NEW p_so_fun=NULL;

    if(g_p_so_handle)
        return FR_SUCC;

    //获取配置
    i_ret_fun = frxml_get2doc_string(APM_CFG_FILENAME_APM, APM_CFG_FRAME_XPATH_REPORT, APM_CFG_FRAME_KEY_LIB, &p_so_name);
    if(i_ret_fun != FR_SUCC || p_so_name == NULL)
    {
        FRLOG_INFO_DETAIL("open cfg[%s][%s][%s] err", APM_CFG_FILENAME_APM, APM_CFG_FRAME_XPATH_REPORT, APM_CFG_FRAME_KEY_LIB);
        goto ERROR;
    }

    frxml_get2doc_string(APM_CFG_FILENAME_APM, APM_CFG_FRAME_XPATH_REPORT, APM_CFG_FRAME_KEY_PARAMS, &p_so_params);

    //加载so
    g_p_so_handle=dlopen(p_so_name, RTLD_LAZY);
    if(g_p_so_handle == NULL)
    {
        FRLOG_WARN_DETAIL("load [%s] err", dlerror());
        goto ERROR;
    }

    //运行模块初始化函数
    p_so_fun   = (FP_CALC_REPORT_NEW) dlsym( g_p_so_handle, APM_REPORT_INFO_NEW);
    if(p_so_fun == NULL)
    {
        FRLOG_WARN_DETAIL("load [%s][%s] err", p_so_name, APM_REPORT_INFO_NEW);
        goto ERROR;
    }

    i_ret_fun = (*p_so_fun)( &g_st_report_module );
    if(i_ret_fun != 0)
    {
        FRLOG_WARN_DETAIL("run [%s][%s] err", p_so_name, APM_REPORT_INFO_NEW);
        goto ERROR;
    }

    if(g_st_report_module.init == NULL)
    {
        FRLOG_WARN_DETAIL("init is null. [%s]", p_so_name);
        goto ERROR;
    }
    else
    {
        i_ret_fun = g_st_report_module.init(&g_st_apm_context, p_so_params, st_apm_params.arr_zmq_connect);
        if(i_ret_fun != FR_SUCC)
        {
            FRLOG_WARN_DETAIL("init err. [%s]", p_so_name);
            goto ERROR;
        }
    }

    if(g_st_report_module.add_data == NULL)
        FRLOG_WARN_DETAIL("add_data is null. [%s]", p_so_name);
    if(g_st_report_module.add_conn == NULL)
        FRLOG_WARN_DETAIL("add_conn is null. [%s]", p_so_name);
    if(g_st_report_module.do_task == NULL)
        FRLOG_WARN_DETAIL("do_task is null. [%s]", p_so_name);
    if(g_st_report_module.destroy == NULL)
        FRLOG_WARN_DETAIL("destroy is null. [%s]", p_so_name);

    if(p_so_params)
        frfree(p_so_params);
    if(p_so_name)
        frfree(p_so_name);
    return FR_SUCC;

ERROR:
    if(g_p_so_handle)
    {
        dlclose(g_p_so_handle);
        g_p_so_handle = NULL;
    }

    if(p_so_params)
        frfree(p_so_params);
    if(p_so_name)
        frfree(p_so_name);
    return FR_FAIL;
}

/*******************************************************************************
*  FUNC     :  释放上报类库,已经相应资源
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
void frreport_mng_destory(void)
{
    if(g_p_so_handle == NULL)
        return;

    if( g_st_report_module.destroy )
        g_st_report_module.destroy();

    dlclose(g_p_so_handle);
    g_p_so_handle = NULL;

    return;
}

/*******************************************************************************
*  FUNC     :  设置report关联信息
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
void frreport_mng_setRef(Report_module *p_st_serviceRef)
{
    if(p_st_serviceRef == NULL)
        return;

    memcpy(p_st_serviceRef, &g_st_report_module, sizeof(Report_module));
    return;
}


/*******************************************************************************
*  FUNC     :  获取l7data里的ip信息
*  ARGS     :
*  RTN      :  成功:FR_SUCC; 失败:FR_FAIL
*  NOTE     :
*******************************************************************************/
FR_STATIC int __frreport_get_ip(Frreport_data *p_st_report_data
                              , u_int *p_ui_src_ip, u_short *p_us_src_port
                              , u_int *p_ui_dst_ip, u_short *p_us_dst_port)
{
    Frreport_body_data *p_st_body_data = NULL;
    Frlist_header *p_lst_hdr=NULL;

    if(p_st_report_data == NULL || p_st_report_data->em_type != FRREPORT_TYPE_L7DATA
       || p_ui_src_ip == NULL || p_us_src_port == NULL
       || p_ui_dst_ip == NULL || p_us_dst_port == NULL)
        return FR_FAIL;

    p_lst_hdr = &p_st_report_data->st_hdr_body;
    p_st_body_data = (Frreport_body_data *)FRLIST_GET_FISRT_NODE_DATA(p_lst_hdr);

    if(p_st_body_data == NULL)
        return FR_FAIL;

    *p_ui_src_ip = p_st_body_data->un_data.st_l7data.src_ip;
    *p_us_src_port = p_st_body_data->un_data.st_l7data.src_port;
    *p_ui_dst_ip = p_st_body_data->un_data.st_l7data.dst_ip;
    *p_us_dst_port = p_st_body_data->un_data.st_l7data.dst_port;

    return FR_SUCC;
}


/*******************************************************************************
*  FUNC     :  上报数据使用lua进行再处理
*  ARGS     :  p_st_ct          (Conntrack *)
*           :  p_st_report_data (Frreport_data *)
*  RTN      : FR_NO:保留上报数据;FR_YES:删除该上报数据
*  NOTE     :
*******************************************************************************/
FR_STATIC int __frreport_mng_lua(Conntrack *p_st_ct, Frreport_data *p_st_report_data)
{
    Lua_cfg *p_st_lua_cfg=NULL;
    u_int   ui_src_ip=0, ui_dst_ip=0;
    u_short us_src_port=0, us_dst_port=0;
    int i_ret_fun=0;

    if(p_st_ct == NULL || p_st_report_data == NULL)
        return FR_NO;

    i_ret_fun = __frreport_get_ip(p_st_report_data, &ui_src_ip, &us_src_port, &ui_dst_ip, &us_dst_port);
    if(i_ret_fun == FR_FAIL)
        return FR_NO;

    frlua_read_lock();
    //没有lua信息时每次查询
    //有lua信息时,版本好发生变化后进行查询
    if( p_st_ct->st_luaInfo.p_lua_cfg == NULL
        || (p_st_ct->st_luaInfo.p_lua_cfg && p_st_ct->st_luaInfo.ul_lua_version != frlua_config_version()) )
    {
        p_st_lua_cfg = NULL;
        if(p_st_ct->st_luaInfo.p_lua_cfg)
            p_st_lua_cfg = (Lua_cfg *)p_st_ct->st_luaInfo.p_lua_cfg;

        //p_st_ct->st_luaInfo.p_lua_cfg = NULL;
        p_st_ct->st_luaInfo.p_lua_cfg = frlua_config_lookup(ui_src_ip, us_src_port);
        if(p_st_ct->st_luaInfo.p_lua_cfg == NULL)
            p_st_ct->st_luaInfo.p_lua_cfg = frlua_config_lookup(ui_dst_ip, us_dst_port);

        if(p_st_lua_cfg)
            frlua_config_put(p_st_lua_cfg);

        if(p_st_ct->st_luaInfo.p_lua_cfg)
            p_st_ct->st_luaInfo.ul_lua_version = frlua_config_version();
    }
    frlua_read_unlock();

    //检查是否存在lua配置
    if(p_st_ct->st_luaInfo.p_lua_cfg == NULL)
        return FR_NO;

    p_st_lua_cfg = (Lua_cfg *)p_st_ct->st_luaInfo.p_lua_cfg;
    if(p_st_lua_cfg->p_lua_handle == NULL)
        return FR_NO;

    pthread_spin_lock(&p_st_lua_cfg->splock);
    i_ret_fun = frlua_do_hook_report(g_p_memstat, p_st_lua_cfg->p_lua_handle, p_st_ct, p_st_report_data);
    pthread_spin_unlock(&p_st_lua_cfg->splock);

    return i_ret_fun;
}

/*******************************************************************************
*  FUNC     :  Frreport_data数据上报
*  ARGS     :  p_st_report_data (Frreport_data *)
*  RTN      :  返回值:成功FR_SUCC, 失败FR_FAIL
*  NOTE     :
*******************************************************************************/
int frreport_mng_save_report_data(Conntrack *p_st_ct, Frreport_data **pp_st_report_data)
{
    int i_ret = FR_FAIL;

    if(pp_st_report_data == NULL || *pp_st_report_data == NULL)
        return FR_FAIL;

    if(g_st_report_module.add_data)
    {
        //ct方向不确定的数据不上报
        if(p_st_ct && p_st_ct->flg_direct_known == FR_DIRECT_KNOWN)
        {
            i_ret = __frreport_mng_lua(p_st_ct, *pp_st_report_data);
            if(i_ret == FR_NO)
                i_ret = g_st_report_module.add_data(pp_st_report_data);
            i_ret = FR_SUCC;
        }
    }

    if(*pp_st_report_data)
        frreport_data_free(pp_st_report_data);

    return i_ret;
}

/*******************************************************************************
*  FUNC     :  Frreport_data数据上报
*  ARGS     :  p_st_report_data (Frreport_data *)
*  RTN      :  返回值:成功FR_SUCC, 失败FR_FAIL
*  NOTE     :
*******************************************************************************/
int frreport_mng_save_report_data2(Frreport_data **pp_st_report_data)
{
    int i_ret = FR_FAIL;

    if(pp_st_report_data == NULL || *pp_st_report_data == NULL)
        return FR_FAIL;

    if(g_st_report_module.add_data )
    {
        i_ret = g_st_report_module.add_data(pp_st_report_data);
    }

    if(pp_st_report_data && *pp_st_report_data)
        frreport_data_free(pp_st_report_data);
    return i_ret;
}

/*******************************************************************************
*  FUNC     :  数据处理线程
*  ARGS     :  arg      (void *arg *)
*  RTN      :
*  NOTE     :
*******************************************************************************/
void *frreport_mng_task(void *arg)
{
    if(g_st_report_module.do_task)
    {
        prctl(PR_SET_NAME,"report_task");
        g_st_report_module.do_task(&g_i_flag_run);
    }

    pthread_exit(0);
    return (void*)NULL;
}

/*******************************************************************************
*  FUNC     :  上报数据开关
*  ARGS     :  void
*  RTN       :
*  NOTE     : console:上报数据显示开关;testtools:上报数据发送到测试工具开关;
*                 writefile:上报数据保存到文件开关;base64:上报数据做base64编码后保存到文件
*******************************************************************************/
void frreport_debug(const char *p_debugstr)
{
    //char *p_debugstr = "console=1 testtools=0 writefile=1 base64=1";
    g_st_report_module.debug_switch(p_debugstr);

    return;
}

/*******************************************************************************
*  FUNC     :  上报信息统计
*  ARGS     :  void
*  RTN       :
*  NOTE     :
*******************************************************************************/
void frreport_stat(char *p_outbuff, unsigned int ui_size)
{
    if(p_outbuff == NULL || ui_size<=0)
        return;

    g_st_report_module.debug_stat(p_outbuff, ui_size);
    return;
}


/*******************************************************************************
*  FUNC     :  上报信息统计
*  ARGS     :  p_st_json (json_object *)
*  RTN       :
*  NOTE     :
*******************************************************************************/
void frreport_stat_json(void *p_st_json)
{
    if( p_st_json == NULL )
        return;

    g_st_report_module.debug_stat_json(p_st_json);
    return;
}
